<template>
    <div>
        <!-- 使用插槽：父级模板里的所有内容都是在父级作用域中编译的；子模板里的所有内容都是在子作用域中编译的。 -->
        <slot></slot>
    </div>
</template>
<script>
    export default {
        name: 'Jitter',
        props: {
            // 抖动范围，单位是px，例如：{x:4, y:2, z:10}
            range: {
                type: Object,
                default: () => {return { z:8 }; },
            },
            start: {
                type: Boolean,
                required: true,
            },
            shiftPercent: {
                type: Number,
                default: 0.1,  // 移动range中初始值的百分比
            },
            perspectiveStyle: {
                type: Object,
                default: () => {
                    return {
                        perspective: '300px',
                        perspectiveOrigin: 'center center'
                    };
                }
            }
        },
        data() {
            return {
                timer: null,
                currentRange: null,
                move: null,
                position: null,
                shiftNumber: null,
                isClockwise: true,   // 向顺时针方向摆动
            };
        },
        mounted() {
            // 如果要执行z轴动画需要设置父级，从而修改子级透视效果，否则z轴没有效果。
            if (this.range.z > 0) {
                const parentEl = this.$el.parentNode;
                Object.keys(this.perspectiveStyle).forEach((key) => {
                    parentEl.style[key] = this.perspectiveStyle[key];
                });
            }
        },
        methods: {
            /**
             * 初始化抖动
             * vm.$emit( event, arg )
             * $emit 绑定一个自定义事件event，当这个这个语句被执行到的时候，就会将参数arg传递给父组件，父组件通过@event监听并接收参数。
             */
            initJitter() {
                this.$emit('update:start', false);
                // 清楚上次动画
                this.clearAnimate();
                /**
                 * 设置currentRange, 填充this.range 中没有的项
                 * Object.assign(target, source1, source2);
                 * Object.assign方法用于对象的合并，将源对象（source）的所有可枚举属性，复制到目标对象（target）。
                 * Object.assign方法的第一个参数是目标对象，后面的参数都是源对象。
                 * 如果目标对象与源对象有同名属性，或多个源对象有同名属性，则后面的属性会覆盖前面的属性。
                 */
                this.currentRange = Object.assign({}, {x: 0, y: 0, z: 0}, this.range);
                // 获取需要操作的项和每次需要摆动的量
                const { position, shiftNumber } = this.getPositionAndShiftNumber();
                this.position = position;
                this.shiftNumber = shiftNumber;
                // 初始move起始点为0
                this.move = { x: 0, y: 0, z: 0};
                // 初始为顺时针
                this.isClockwise = true;
                // 执行动画
                this.timer = window.requestAnimationFrame(this.continueJitter);
            },
            /**
             * 获取需要操作的的项 和 每次需要摆动的量
             */
            getPositionAndShiftNumber() {
                const result = {
                    position: [],
                    shiftNumber: {}
                };
                const range = this.currentRange;
                let val;
                for (const key in range) {
                    val = range[key];
                    if (val > 0) {
                        result.position.push(key);
                        result.shiftNumber[key] = this.shiftPercent * val;
                    }
                }
                console.log(result,'result');
                return result;
            },
            /**
             *  持续抖动
             */
            continueJitter() {
                this.refreshMove(this.isClockwise ? -1 : 1);
                const absMove = this.getAbsMove();
                const currentRange = this.currentRange;
                let changeDirection = false;
                for (let i =0, l = this.position.length, p; i < l; i += 1) {
                    p = this.position[i];
                    if(currentRange[p] <= absMove[p]) {
                        this.currentRange[p] -= this.shiftNumber[p];
                        if(this.currentRange[p] <= 0) {
                           this.jitterView({x: 0, y: 0, z: 0 }) ;
                           this.clearAnimate();
                           return;
                        }
                        this.move[p] = this.isClockwise ? -this.currentRange[p] : this.currentRange[p];
                        changeDirection = true;
                    }
                }
                if(changeDirection) {
                    this.isClockwise = !this.isClockwise;
                }
                this.jitterView(this.move);
                this.timer = window.requestAnimationFrame(this.continueJitter);
            },
            /**
             * 更新元素摆动的位移
             */
            refreshMove(direction) {
                const shiftNumber = this.shiftNumber;
                this.position.forEach((item) => {
                    this.move[item] += direction * shiftNumber[item];
                })
            },
            /**
             * 获取移动时的绝对值
             */
            getAbsMove() {
                const absMove = {};
                for (const m in this.move) {
                absMove[m] = Math.abs(this.move[m]);
                }
                return absMove;
            },
             /**
             * 修改元素位置
             * @param x
             * @param y
             * @param z
             */
            jitterView({ x = 0, y = 0, z = 0 }) {
                this.$el.style.transform = `translate3d(${x}px, ${y}px, ${z}px)`;
            },
             /**
             * 清除动画
             */
            clearAnimate() {
                if (this.timer) {
                window.cancelAnimationFrame(this.timer);
                }
            },
        },
        destroyed() {
        this.clearAnimate();
        },
        watch: {
            start(newVal) {
                // 开始抖动
                if (newVal) {
                this.initJitter();
                }
            }
        }
    }
</script>